﻿using System;
using System.IO;
using System.Linq;

namespace ServerCrafter.ClassLibrary
{
    /// <summary>
    /// The properties manager manages creating a new properties file if one does not exist,
    /// geting the data of the properties manager with the Get() method and
    /// appling changes to the properties file with the Set() method.
    /// </summary>
    public static class PropertiesManager
    {
        private static readonly string Path = AppDomain.CurrentDomain.BaseDirectory + @"/server.properties";
        public static string[,] Data;
        public static int lineCount;

        private static StreamReader propertiesReader;
        private static StreamWriter propertiesWriter;

        /// <summary>
        /// Initializes the properties manager. Creates the properties file if it does not exist.
        /// </summary>
        public static void Initialize()
        {
            Logger.WriteInfo("Initializing properties manager.");

            if (File.Exists(Path)) return;

            Logger.WriteInfo("No server.properties file exists. Writing one now.");
            try
            {
                File.WriteAllBytes(Path, Resources.PropertiesFile);
            }
            catch (PathTooLongException exception)
            {
                Logger.WriteWarning(
                    "Could not write properties file! The specified path, file name, or both exceed the system-defined maximum length.");
                Logger.WriteWarning("\tException: " + exception);
            }
            catch (DirectoryNotFoundException exception)
            {
                Logger.WriteWarning(
                    "Could not write properties file! The specified path is invalid (for example, it is on an unmapped drive).");
                Logger.WriteWarning("\tException: " + exception);
            }
            catch (IOException exception)
            {
                Logger.WriteWarning("Could not write properties file! An I/O error occurred while opening the file.");
                Logger.WriteWarning("\tException: " + exception);
            }
            catch (Exception exception)
            {
                Logger.WriteSevere("Could not write properties file! An unknown exception has occured.");
                Logger.WriteSevere("\tException: " + exception);
            }
        }

        /// <summary>
        /// Gets the contents of the properties file and returns it.
        /// </summary>
        /// <returns></returns>
        public static string[,] Get()
        {
            Logger.WriteInfo("Getting server properties file data now.");
            lineCount = File.ReadAllLines(Path).Count();
            Data = new string[lineCount, 2];
            Logger.WriteInfo("Properties file has " + lineCount + " lines.");

            Logger.WriteInfo("Initializing stream reader for file " + Path + ".");
            try
            {
                using (propertiesReader = new StreamReader(Path))
                {
                    var lineNumber = 0;
                    string line;

                    while ((line = propertiesReader.ReadLine()) != null)
                    {
                        if (!line.Contains('=')) continue;

                        Data[lineNumber, 0] = line.Split('=')[0];
                        Data[lineNumber, 1] = line.Split('=')[1];

                        lineNumber++;
                    }
                }
            }
            catch (OutOfMemoryException exception)
            {
                Logger.WriteWarning(
                    "Could not read properties file! There is insufficient memory to allocate a buffer for the returned string.");
                Logger.WriteWarning("\tException: " + exception);
            }
            catch (IOException exception)
            {
                Logger.WriteWarning("Could not read properties file! An I/O error has occured");
                Logger.WriteWarning("\tException: " + exception);
            }
            catch (Exception exception)
            {
                Logger.WriteWarning("Could not read properties file! An unknown exception has occured");
                Logger.WriteWarning("\tException: " + exception);
            }

            Logger.WriteInfo("Finished reading the file. Closing the stream reader.");
            propertiesReader.Close();
            Logger.WriteInfo("Finished closing the stream reader. Disposing of the stream reader.");
            propertiesReader.Dispose();
            Logger.WriteInfo("Finished disposing of the stream reader.");

            return Data;
        }

        /// <summary>
        /// Applies the change of properties data to the properties file.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="value">The value.</param>
        public static void Set(int index, string value)
        {
            Data[index, 1] = value;
            Logger.WriteInfo("Applying changes to properties file. Setting " + Data[index, 0] + " to " + Data[index, 1] + ".");
            var lineNumber = 0;

            using (propertiesWriter = new StreamWriter(Path, false))
            {
                try
                {
                    while (lineNumber < (lineCount - 1))
                    {
                        if (Data[lineNumber, 0] == null) { lineNumber++; continue; }

                        propertiesWriter.WriteLine(Data[lineNumber, 0] + "=" + Data[lineNumber, 1]);
                        lineNumber++;
                    }
                }
                catch (IOException exception)
                {
                    Logger.WriteWarning("Could not write to file! An I/O error has occured.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (ObjectDisposedException exception)
                {
                    Logger.WriteWarning("Could not write to file! The text write is closed.");
                    Logger.WriteWarning("\tException: " + exception);
                }
                catch (Exception exception)
                {
                    Logger.WriteWarning("Could not write to file! An unknown exception has occured.");
                    Logger.WriteWarning("\tException: " + exception);
                }
            }
            Logger.WriteInfo("Finished writing to file. Closing the stream writer.");
            propertiesWriter.Close();
            Logger.WriteInfo("Finished closing the stream writer. Disposing of the stream writer.");
            propertiesWriter.Dispose();
            Logger.WriteInfo("Finished disposing of the stream writer.");
        }
    }
}